package com.xqq.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xqq.dao.ConflictEventDAO;
import com.xqq.dao.ConflictThermolDAO;
import com.xqq.entity.ConflictEventDO;
import com.xqq.entity.ConflictThermolDO;
import org.apache.commons.math3.ml.clustering.CentroidCluster;
import org.apache.commons.math3.ml.clustering.Cluster;
import org.apache.commons.math3.ml.clustering.Clusterable;
import org.apache.commons.math3.ml.clustering.KMeansPlusPlusClusterer;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Service
public class HeatmapService implements ApplicationRunner {


    @Resource
    private ConflictThermolDAO conflictThermolDAO;

    @Resource
    private ConflictEventDAO conflictEventDAO;

    @Transactional
    public List<ConflictThermolDO> generateHeatmap(int numClusters) {
        long startTime = System.currentTimeMillis();
        // Step 1: Fetch data from the database
        List<ConflictEventDO> records = conflictEventDAO.list();
        System.out.println(records.size());
        // Step 2: Prepare data for clustering
        List<GeoPoint> geoPoints = new ArrayList<>();
        for (ConflictEventDO record : records) {
            geoPoints.add(new GeoPoint(record.getLatitude(), record.getLongitude()));
        }

        // Step 3: Perform K-means clustering
        KMeansPlusPlusClusterer<GeoPoint> clusterer = new KMeansPlusPlusClusterer<>(numClusters);
        List<CentroidCluster<GeoPoint>> cluster1 = clusterer.cluster(geoPoints);

        // Step 4: Prepare results
        List<ConflictThermolDO> results = new ArrayList<>();
        for (Cluster<GeoPoint> cluster : cluster1) {
            double[] centroid = new double[2];
            for (GeoPoint point : cluster.getPoints()) {
                centroid[0] += point.getPoint()[0];
                centroid[1] += point.getPoint()[1];
            }
            centroid[0] /= cluster.getPoints().size();
            centroid[1] /= cluster.getPoints().size();
            ConflictThermolDO thermolDO = new ConflictThermolDO();
            thermolDO.setLatitude(Double.parseDouble(String.format("%.6f", centroid[0])));
            thermolDO.setLongitude(Double.parseDouble(String.format("%.6f", centroid[1])));
            thermolDO.setCount(cluster.getPoints().size());
            results.add(thermolDO);
        }
        System.out.println(Arrays.toString(results.toArray()));
        conflictThermolDAO.remove(new LambdaQueryWrapper<>());
        conflictThermolDAO.saveBatch(results);
        System.out.println("Time taken: " + (System.currentTimeMillis() - startTime) + " ms");
        return results;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
//        generateHeatmap(300);
    }


    static class GeoPoint implements Clusterable {
        private final double[] points;

        public GeoPoint(double latitude, double longitude) {
            this.points = new double[]{latitude, longitude};
        }

        @Override
        public double[] getPoint() {
            return points;
        }
    }

}
